package model

import (
	"context"
	"dag2.0/gat1400-proxy/logger"
	"dag2.0/gat1400-proxy/util"
	"database/sql/driver"
	"errors"
	"fmt"
	jsoniter "github.com/json-iterator/go"
	uuid "github.com/satori/go.uuid"
	"net/http"
	"strings"
	"time"
)

const (
	URL_REGIST              = "http://%s:%s/VIID/System/Register"
	URL_KEEPALIVE           = "http://%s:%s/VIID/System/Keepalive"
	REGIST                  = "/VIID/System/Register"
	AUTHORIZATION           = "WWW-Authenticate"
	NONCE                   = "nonce"
	QOP                     = "qop"
	REALM                   = "realm"
	OPAQUE                  = "opaque"
	RESPONSE                = "response"
	ALGORITHM               = "algorithm"
	URI                     = "uri"
	NC                      = "nc"
	CNONCE                  = "cnonce"
	RESOURCE_TYPE_EQUIPMENT = "1"
	RESOURCE_TYPE_TOLLGATE  = "2"
	RESOURCE_TYPE_LANE      = "3"
)

//资源增删改结构体
type ViewInfo struct {
	ManagerID       string `json:"ManagerID"`
	IncEquipmentIds string `json:"IncEquipmentIds"` //新增设备
	DecEquipmentIds string `json:"DecEquipmentIds"` //删除设备
	IncTollgateIds  string `json:"IncTollgateIds"`  //新增卡口
	DecTollgateIds  string `json:"DecTollgateIds"`  //删除卡口
	TaskIds         string `json:"TaskIds"`
}

//资源信息结构体
type ViewInfoVo struct {
	ManagerID    string   `json:"ManagerID"`
	EquipmentIds []string `json:"EquipmentIds"`
	TollgateIds  []string `json:"TollgateIds"`
	TaskIds      []string `json:"TaskIds"`
}

type ViewLibList struct {
	ViewLibList []*ViewLib
}

type DevicePush struct {
	ResourceId   string `json:"resourceId" db:"resourceId"`
	PushTime     int64  `json:"pushTime" db:"pushTime"`
	ResourceType string `json:"resourceType" db:"resourceType"`
	MangerId     string `json:"managerId" db:"managerId"`
}
type ViewLib struct {
	MangerID   string `json:"ManagerId" db:"MANAGERID"`    //视图库id
	Type_      string `json:"Type" db:"TYPE_"`             //0-设备，1-卡口，2-任务
	ResourceID string `json:"ResourceId" db:"RESOURCE_ID"` //设备id或卡口id
}

func (v *ViewLib) Value() (driver.Value, error) {
	return []interface{}{v.MangerID, v.Type_, v.ResourceID}, nil
}

type SubscribeManager struct {
	ManagerID     string `json:"ManagerID" db:"manager_id"`
	SelfManagerID string `json:"SelfManagerID" db:"self_manager_id"`
	AreaCode      string `json:"AreaCode" db:"area_code"`
	AreaName      string `json:"AreaName" db:"area_name"`
	ManagerName   string `json:"ManagerName" db:"manager_name"`
	ManagerIp     string `json:"ManagerIp" db:"manager_ip"`
	ManagerPort   string `json:"ManagerPort" db:"manager_port"`
	ReceiveAddr   string `json:"ReceiveAddr" db:"receive_addr"`
	UserName      string `json:"UserName" db:"user_name"`
	Password      string `json:"Password" db:"password"`
	IsOnline      string `json:"IsOnline" db:"is_online"`
	DeviceStatus  int    `json:"DeviceStatus" db:"device_status"`
	DataStatus    int    `json:"DataStatus" db:"data_status"`
	ManagerRemark string `json:"ManagerRemark" db:"manager_remark"`
	UpdateTime    string `json:"UpdateTime" db:"update_time"`
	InsertTime    string `json:"InsertTime" db:"insert_time"`
	ManagerType   string `json:"ManagerType" db:"manager_type"`
}

type SubscribeManagerEx struct {
	*SubscribeManager
	EquipmentIds    []string `json:"EquipmentIds"`
	TollgateIds     []string `json:"TollgateIds"`
	TaskIds         []string `json:"TaskIds"`
	IsRegister      bool     `json:"isRegister"`
	RegisterSuccess bool     `json:"registerSuccess"`

	ExecuteOperation int                `json:"ExecuteOperation"` //更新项目 连云港地标扩展  1-添加 2-修改 3-删除
	Ctx              context.Context    `json:"-"`
	Cancel           context.CancelFunc `json:"-"`
}

func (manager *SubscribeManagerEx) Register() {
OUTLOOP:
	for {
		select {
		case <-manager.Ctx.Done():
			break OUTLOOP
		default:
			if !manager.RegisterSuccess {
				manager.RegisterAction()
			}
			time.Sleep(time.Second * time.Duration(30))
		}
	}
}
func (manager *SubscribeManagerEx) RegisterAction() {
RegisterLoop:
	//第一次注册
	registerModel := BuildGat1400RegisterObj(manager.SelfManagerID)
	bytes, head, statusCode, err := util.Request(fmt.Sprintf(URL_REGIST, manager.ManagerIp, manager.ManagerPort), http.MethodPost, map[string]string{
		"Content-Type":  "application/VIID+JSON",
		"User-Identify": manager.SelfManagerID,
	}, registerModel, nil)
	if err != nil {
		logger.LOG_ERROR("注册接口调用失败", err)
		return
	}

	//if bytes != nil && len(bytes) > 0 {
	//	response := AnalyzeRsponseContent(bytes)
	//	resJson, _ := jsoniter.Marshal(response)
	//	//失败
	//	if response == nil || len(response.ResponseStatusListObject.ResponseStatusObject) <= 0 ||
	//		response.ResponseStatusListObject.ResponseStatusObject[0] == nil ||
	//		response.ResponseStatusListObject.ResponseStatusObject[0].StatusCode != OK {
	//		logger.LOG_ERROR("注册失败", util.BytesString(resJson)) //等待下次重新注册
	//		return
	//	}
	//}
	switch statusCode {
	case http.StatusOK, http.StatusCreated, http.StatusNoContent:
		//无需再次注册
		manager.RegisterSuccess = true
		return
	case http.StatusUnauthorized:
		var authorizationStr string
		headers := head.Get(AUTHORIZATION)
		//Digest realm="myrealm",qop="auth" Digest realm="myrealm",qop="auth",nonce="6a0526b08a2a"
		logger.LOG_DEBUG("第一次注册返回的header ： ", headers)
		digests := strings.Split(strings.Trim(headers, " "), "Digest ")
		for _, digest := range digests {
			if strings.Index(digest, NONCE) != -1 {
				authorizationStr = digest
			}
		}
		if !(len(authorizationStr) > 0) {
			logger.LOG_ERROR("第一次注册返回的header中未包含nonce,注册失败", errors.New("注册失败，开始重新注册"))
			return
		}
		var authorizationMap = make(map[string]string)
		for _, argument := range strings.Split(authorizationStr, ",") {
			//var parm string
			//if strings.Contains(argument, "realm") {
			//	parm = strings.ReplaceAll(strings.ReplaceAll(argument, `"`, ""), `'`, "")
			//} else {
			//	parm = strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(argument, `"`, ""), `'`, ""), " ", "")
			//}
			parm := strings.Trim(strings.ReplaceAll(strings.ReplaceAll(argument, `"`, ""), `'`, ""), " ")
			if strings.Index(parm, "=") == -1 {
				logger.LOG_ERROR("header 参数错误", parm)
				continue
			}
			authorizationMap[parm[:strings.Index(parm, "=")]] = parm[strings.Index(parm, "=")+1:]
		}
		body, _, _, err := manager.PostRegistSecond(authorizationMap)
		if err != nil {
			return
		}
		responseModel := AnalyzeRsponseContent(body)
		if responseModel == nil || len(responseModel.ResponseStatusListObject.ResponseStatusObject) < 1 ||
			responseModel.ResponseStatusListObject.ResponseStatusObject[0] == nil ||
			responseModel.ResponseStatusListObject.ResponseStatusObject[0].StatusCode != "0" {
			logger.LOG_ERROR(fmt.Sprintf("第二次注册失败:返回数据为：%s", util.BytesString(body)), err)
			return
		}
		manager.RegisterSuccess = true
	case http.StatusMovedPermanently, http.StatusFound:
		//重定向，再次注册
		location := head.Get("Location")
		if !(len(location) > 0) {
			logger.LOG_ERROR("重定向地址为空", errors.New("重定向地址为空"))
			return
		}
		logger.LOG_WARN("重定向地址为： ", location)
		uri := strings.Split(strings.Trim(strings.ReplaceAll(strings.ReplaceAll(location, "http://", ""), "/VIID/System/Register", ""), ""), ":")
		if len(uri) != 2 {
			logger.LOG_ERROR(fmt.Sprintf("重定向地址错误，location is : %s", location), errors.New("重定向地址错误"))
			return
		}
		manager.ManagerIp = uri[0]
		manager.ManagerPort = uri[1]
		goto RegisterLoop
	default:
		logger.LOG_ERROR(fmt.Sprintf("注册失败，返回码为 : %d,返回信息为：%s", statusCode, string(bytes)), errors.New("注册失败"))
		return
	}
}

func (s *SubscribeManagerEx) PostRegistSecond(authorizationMap map[string]string) ([]byte, http.Header, int, error) {
	//使用md5加密
	authorizationMap[ALGORITHM] = "MD5"
	authorizationMap[NC] = "00000001"
	authorizationMap[CNONCE] = strings.ReplaceAll(uuid.NewV4().String(), "-", "")[0:12]
	// 计算response
	//HA1 = MD5(A1) = MD5(name:realm:password)
	HA1 := util.MD5(s.UserName + ":" + authorizationMap[REALM] + ":" + s.Password)
	//HA2 = MD5(A2) = MD5(method:uri)
	HA2 := util.MD5("POST" + ":" + REGIST)
	//response = MD5(HA1:nonce:nc:cnonce:qop:HA2)
	logger.LOG_WARN("authorizationMap[QOP]  is  :", authorizationMap[QOP])
	str := HA1 + ":" + authorizationMap[NONCE] + ":" + authorizationMap[NC] + ":" + authorizationMap[CNONCE] + ":" + authorizationMap[QOP] + ":" + HA2
	logger.LOG_DEBUG("加密前的responseString : ", str)
	md5str2 := util.MD5(str)
	logger.LOG_INFO("加密后的responseString : ", md5str2)
	authorizationMap[RESPONSE] = md5str2
	// 构建返回的认证头Authorization
	//eg:Digest username='chongqing', realm='myrealm', nonce='0efb398cf026',
	//uri='/VIID/System/Register', response='66254bd928831fd5e5a72a1a509d8235', algorithm='MD5',
	//qop=auth, nc=00000001, cnonce='7cb15baf061f',opaque='df387b3a3e534e8c801c3a6ebc9d65d4'
	var authorizationBuild strings.Builder
	authorizationBuild.WriteString("Digest ")
	authorizationBuild.WriteString(fmt.Sprintf(`%s="%s",`, URI, REGIST))
	for authorizationKey, authorizationValue := range authorizationMap {
		//nc 无引号
		logger.LOG_INFO("authorizationKey: ", authorizationKey, "   authorizationValue: ", authorizationValue)
		if authorizationKey == NC || authorizationKey == QOP {
			logger.LOG_INFO("====== authorizationKey: ", authorizationKey, "   authorizationValue: ", authorizationValue)
			authorizationBuild.WriteString(fmt.Sprintf(`%s=%s,`, authorizationKey, authorizationValue))
			continue
		}
		authorizationBuild.WriteString(fmt.Sprintf(`%s="%s",`, authorizationKey, authorizationValue))
	}
	authorizationBuild.WriteString(fmt.Sprintf(`%s="%s"`, "username", s.UserName))
	logger.LOG_INFO("第二次请求Authorization : ", authorizationBuild.String())
	registerModel := BuildGat1400RegisterObj(s.SelfManagerID)
	logger.LOG_INFO("第二次请求url is :  ", fmt.Sprintf(URL_REGIST, s.ManagerIp, s.ManagerPort))
	logger.LOG_INFO("第二次请求model is :  ", registerModel.toString())
	data, head, code, err := util.Request(fmt.Sprintf(URL_REGIST, s.ManagerIp, s.ManagerPort), http.MethodPost, map[string]string{
		"Content-Type":  "application/VIID+JSON",
		"User-Identify": s.SelfManagerID,
		"Authorization": authorizationBuild.String(),
	}, registerModel, nil)
	if err != nil {
		logger.LOG_ERROR("第二次注册 请求失败", err)
		return nil, nil, -1, err
	}
	return data, head, code, nil
}

func (manager *SubscribeManagerEx) KeepAlive() {
OUTLOOP:
	for {
		select {
		case <-manager.Ctx.Done():
			break OUTLOOP
		default:
			if manager.RegisterSuccess {
				manager.KeepAliveAction()
			}
			time.Sleep(time.Second * time.Duration(30))
		}
	}
}
func (manager *SubscribeManagerEx) KeepAliveAction() {
	keepAlived := BuildGat1400KeepaliveObject(manager.SelfManagerID)
	bytes, _, resCode, err := util.Request(fmt.Sprintf(URL_KEEPALIVE, manager.ManagerIp, manager.ManagerPort), http.MethodPost, map[string]string{
		"Content-Type":  "application/VIID+JSON",
		"User-Identify": manager.SelfManagerID,
	}, keepAlived, nil)
	if err != nil || (resCode != http.StatusOK && resCode != http.StatusCreated && resCode != http.StatusNoContent) {
		logger.LOG_ERROR("保活失败", err)
		manager.RegisterSuccess = false
		return
	}
	if bytes != nil && len(bytes) > 0 {
		response := AnalyzeRsponseContent(bytes)
		resJson, _ := jsoniter.Marshal(response)
		//失败
		if response == nil || len(response.ResponseStatusListObject.ResponseStatusObject) <= 0 ||
			response.ResponseStatusListObject.ResponseStatusObject[0] == nil ||
			response.ResponseStatusListObject.ResponseStatusObject[0].StatusCode != OK {
			logger.LOG_ERROR("保活失败", util.BytesString(resJson))
			//重新注册
			manager.RegisterSuccess = false
			return
		}
	}
}
